<?php
/**
 * Created by PhpStorm.
 * User: ari
 * Date: 9/29/14
 * Time: 9:10 PM
 */
namespace BC\PGP;

use BC\PGP\Exceptions\PGPPrivateKeyNotFound;
use BC\PGP\PGPUtil;
use BC\SiteIndex;
use BC\User\Responses\ProfileResponse;
use BC\User\Session\SessionManager;
use CPath\Build\IBuildable;
use CPath\Build\IBuildRequest;
use CPath\Render\HTML\Theme\ThemedClassAttributes;
use CPath\Request\Cookie\ICookieRequest;
use CPath\Request\Exceptions\RedirectException;
use CPath\Request\Exceptions\RequestException;
use CPath\Request\Executable\IExecutable;
use CPath\Request\Form\IFormRequest;
use CPath\Request\IRequest;
use CPath\Request\Log\StaticLogger;
use CPath\Request\Parameter\RequiredParameter;
use CPath\Request\Session\ISessionRequest;
use CPath\Request\Validation\FormValidation;
use CPath\Response\IResponse;
use CPath\Route\IRoutable;
use CPath\Route\RouteBuilder;

class KeyRoute implements IExecutable, IBuildable, IRoutable
{
	const FINGERPRINT_LENGTH = 16;
	const PATH_FORMAT = '/key/%s';

    const PARAM_FINGERPRINT = 'fingerprint';
    const PARAM_IMPORT = 'import';

    private $mFingerprint;
	private $mForm;

	private $paramFingerprint;
	private $paramImport;

    public function __construct($fingerprint) {
        $this->mFingerprint = $fingerprint;
//	    $this->mForm = new HTMLForm('POST', 'register',
//		    $this->paramFingerprint = new RequiredParameter(self::PARAM_FINGERPRINT),
//		    $this->paramImport = new RequiredParameter(self::PARAM_IMPORT)
//	    );
    }

	public function getRouteUrl() {
		$hash = substr(preg_replace('/[^\w]/', '', $this->getFingerprint()), -self::FINGERPRINT_LENGTH);
		$path = sprintf(self::PATH_FORMAT, $hash);
		return $path;
	}

    public function getFingerprint() {
        return $this->mFingerprint;
    }

	public function findPublicKey(IRequest $Request) {
		if(!$Request instanceof ISessionRequest)
			throw new RequestException("Request does not implement ISessionRequest");

		$SessionManager = new SessionManager($Request);
		$UserProfile = $SessionManager->getActiveUserProfile();

		$Keys = $UserProfile->getPublicKeys();
		$PublicKeyFile = null;
		foreach($Keys as $Key) {
			if($Key->getFingerprint() === $this->mFingerprint) {
				return $Key;
				break;
			}
		}

		throw new RequestException("No public key file found matching fingerprint: " . $this->mFingerprint);
	}


	/**
	 * Execute a command and return a response. Does not render
	 * @param \CPath\Request\Form\IFormRequest|\CPath\Request\IRequest $Request the request to execute
	 * @param \BC\PGP\PublicKeyFile $PublicKeyFile
	 * @param null $privateKeyString
	 * @throws \CPath\Request\Exceptions\RequestException
	 * @return ProfileResponse the execution response
	 */
	function execute(IRequest $Request, PublicKeyFile $PublicKeyFile=null, $privateKeyString=null) {
		if(!$PublicKeyFile)
			$PublicKeyFile = $this->findPublicKey($Request);

		if($PublicKeyFile->getFingerprint() !== $this->mFingerprint)
			throw new RequestException("Public key does not match fingerprint: " . $this->mFingerprint);

        if(!$privateKeyString)
	        $privateKeyString = $Request->getFormValue(self::PARAM_IMPORT, "Import private key", $Request::FLAG_TEXTAREA);

        $PrivateKey = new PrivateKey($privateKeyString);

        if($PrivateKey->getFingerprint() !== $this->mFingerprint)
            throw new RequestException("Imported key does not match fingerprint");
        $Request->log("Private key fingerprint matched: " . $PrivateKey->getFingerprint());

        if ($Request->getMethodName() === 'GET')
            throw new RequestException("");

        $Util = new PGPUtil($Request);
        try {
            $oldPK = $Util->exportPrivateKey($PrivateKey->getFingerprint());
            $Request->log("Secret key exists. Skipping import");
            //$Util->deleteSecretKey($PrivateKey->getFingerprint());
            //throw new RequestException("Private key already imported: " . $PrivateKey->getFingerprint());
        } catch (PGPPrivateKeyNotFound $ex) {
            $privateKeyString = $PrivateKey->exportKey();
            $Util->importPrivateKey($privateKeyString);
        }

        if($Request instanceof ICookieRequest)
            self::sendCookie($Request, $PrivateKey);

        $redirectPath = self::COOKIE_PATH . '/' . $PrivateKey->getFingerprint();
        return new RedirectException("Import complete. Redirecting...", $redirectPath);
    }

//    /**
//     * Validate a form request
//     * @param IFormRequest $Request
//     * @return mixed the validated data
//     * @throws RequestException if validation fails
//     */
//    function validateForm(IFormRequest $Request) {
//        $Form = new FormValidation();
//        $Form->req(self::PARAM_IMPORT,         "Import private key");
//        return $Form->validateForm($Request);
//    }

    // Static

    static function sendCookie(ICookieRequest $Request, PrivateKey $PrivateKey) {
        $fingerprint = $PrivateKey->getFingerprint();
        $sent = $Request->sendCookie('pk', $PrivateKey->exportKey(), 0, self::COOKIE_PATH . '/' . $fingerprint);

        if ($sent)
            $Request->log("Private key cookie created successfully");
        else
            $Request->log("Private key cookie failed to set", $Request::ERROR);

        return $sent;
    }

    /**
     * Handle this request and render any content
     * @param IBuildRequest $Request the build request inst for this build session
     * @return void
     * @build --disable 1
     * Note: Use doctag 'build' with '--disable 1' to have this IBuildable class skipped during a build
     */
    static function handleBuildStatic(IBuildRequest $Request) {
//        $RouteBuilder = new RouteBuilder($Request, new SiteIndex());
//        $RouteBuilder->writeRoute('ANY ' . self::COOKIE_PATH . '/:' . self::PARAM_FINGERPRINT, __CLASS__); // Pass this class as arg to renderer
//        $RouteBuilder->writeRoute('ANY ' . self::COOKIE_PATH . '/', __CLASS__); // Pass this class as arg to renderer
    }

	/**
	 * Route the request to this class object and return the object
	 * @param IRequest $Request the IRequest inst for this render
	 * @param Object[]|null $Previous all previous response object that were passed from a handler, if any
	 * @param null|mixed $_arg [varargs] passed by route map
	 * @throws RequestException
	 * @return FormValidation|IResponse
	 * If nothing is returned (or bool[true]), it is assumed that rendering has occurred and the request ends
	 * If false is returned, this static handler will be called again if another handler returns an object
	 * If an object is returned, it is passed along to the next handler
	 */
    static function routeRequestStatic(IRequest $Request, Array &$Previous = array(), $_arg=null) {
        StaticLogger::start($Request);

        $GET = new FormValidation();
        $GET->req(self::PARAM_FINGERPRINT,    "Enter key fingerprint");
        list($fingerprint) = $GET->validateRequest($Request);

        //$redirectPath = self::COOKIE_PATH . '/' . $fingerprint;

//        if($Request->getPath() !== $redirectPath) {
//            throw new RedirectException("Redirecting to key path...", $redirectPath, 2);
//        }

        $Key = new KeyRoute($fingerprint);

	    $Form = new FormValidation();
	    $Form->req(self::PARAM_IMPORT,         "Import private key");

	    if(!$Request instanceof IFormRequest) // GET-style activeRequests return the form for rendering
		    return $Form;

	    $values = $Form->validateRequest($Request);
        $Response = $Key->execute($Request, null, $values[self::PARAM_IMPORT]);
        return $Response;
    }
}
